/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "ThreadUtil.h"
#include "Tracer.h"
#include <sys/errno.h>

namespace libemb{
MutexLock::MutexLock()
{
	pthread_mutex_init(&m_mutex,NULL);
}

MutexLock::~MutexLock()
{
	pthread_mutex_destroy(&m_mutex);
}

/**
 *  \brief  互斥锁上锁
 *  \note   none
 */
int MutexLock::lock()
{
	return pthread_mutex_lock(&m_mutex);
}
/**
 *  \brief  互斥锁解锁
 *  \note   none
 */
int MutexLock::unLock()
{
	return pthread_mutex_unlock(&m_mutex);
}
/**
 *  \brief  互斥锁尝试上锁
 *  \note   none
 */
int MutexLock::tryLock()
{
	return pthread_mutex_trylock(&m_mutex);
}

AutoLock::AutoLock(MutexLock* mutexLock):
m_mutexLock(mutexLock)
{
	m_mutexLock->lock();
}

AutoLock::~AutoLock()
{
	m_mutexLock->unLock();
}

MutexCond::MutexCond(MutexLock* lock)
{
    m_lock = lock;
    #if defined OS_CYGWIN || defined OS_UNIX
    pthread_condattr_init(&m_condAttr); 
    pthread_condattr_setclock(&m_condAttr, CLOCK_MONOTONIC);
    #endif
    pthread_cond_init(&m_cond,&m_condAttr);
}

MutexCond::~MutexCond()
{
}
/**
 *  \brief  等待条件变量
 *  \param  int usec,超时时间,<0时阻塞等待,>=0时等待usec
 *  \return 成功等到条件变量返回STATUS_OK,超时返回STATUS_TIMEOUT,失败返回STATUS_ERROR
 *  \note   执行该方法后线程会阻塞并释放锁,一直等待条件变量或者超时.
 */
int MutexCond::wait(int usec)
{
    int ret;
    if (usec>=0){
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
		time_t sec = usec/1000000;
		if (sec>0)
        {
        	ts.tv_sec += sec;
		}
		long nsec = (usec%1000000)*1000;
		ts.tv_nsec += nsec;
		if (ts.tv_nsec>=1000000000)
		{
			ts.tv_sec += 1;
			ts.tv_nsec = nsec -1000000000;
		}
    #if defined OS_CYGWIN || defined OS_UNIX
        ret = pthread_cond_timedwait(&m_cond,&(m_lock->m_mutex),&ts);
    #else
        ret = pthread_cond_timedwait_monotonic_np(&m_cond,&(m_lock->m_mutex),&ts);/* for Android bionic pthread */
    #endif
    } else {
        ret = pthread_cond_wait(&m_cond,&(m_lock->m_mutex));
    }
    switch (ret) {
    case 0:
        return STATUS_OK;
    case ETIMEDOUT:
        return STATUS_TIMEOUT;
    default:
        return STATUS_ERROR;
    }
}
/**
 *  \brief  满足条件变量,通知等待者
 *  \note   none
 */
int MutexCond::meet()
{
    return (pthread_cond_signal(&m_cond)==0)?STATUS_OK:STATUS_ERROR;

}


Semaphore::Semaphore(char* name):
m_name("")
{
	if (name!=NULL)
	{
		m_name = std::string(name);
	}
}

Semaphore::~Semaphore()
{
}

/**
 *  \brief  打开信号量
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   信号量存在,则返回,不存在则创建,创建成功生成/tmp/sem.name
 */
bool Semaphore::open(int value)
{
	if (m_name.empty())
	{
		if (-1==sem_init(&m_sem, 0, value))
	    {
	        TRACE_ERR_CLASS("sem init error:%s\n",ERROR_STRING);
	        return false;
	    }
	}
    else
    {
	    sem_t* sem = sem_open(CSTR(m_name),O_EXCL);
	    if (sem==SEM_FAILED)
	    {  
	        sem = sem_open(CSTR(m_name),O_CREAT,0644,value);
	        if (sem==SEM_FAILED)
	        {
	            TRACE_ERR_CLASS("sem create error:%s\n",ERROR_STRING);
	            return false;
	        }
	    }
	    m_sem = *sem;
    }
	return true;
}
/**
 *  \brief  关闭信号量
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   当信号量不再使用时,请关闭.进程退出时也会自动关闭,
 *          但关闭信号量不会真正删除信号量,删除需要使用sem_unlink.
 */
bool Semaphore::close()
{
	if (m_name.empty())
	{
		if (-1==sem_destroy(&m_sem))
	    {
	        TRACE_ERR_CLASS("sem destroy error:%s\n",ERROR_STRING);
	        return false;
	    }
	}
	else
    {
	    if (-1==sem_close(&m_sem))
	    {
	        TRACE_ERR_CLASS("sem close error:%s\n",ERROR_STRING);
	        return false;
	    }
	}
	return true;
}

/**
 *  \brief  等待信号量
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   使信号量值减1,如果无资源可申请,则阻塞.
 */
bool Semaphore::wait()
{
    if (0==sem_wait(&m_sem))
    {
        return true;
    }
    return false;
}
/**
 *  \brief  尝试等待信号量
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   使信号量值减1,如果无资源可申请,则返回.
 */
bool Semaphore::tryWait()
{
    if (0==sem_trywait(&m_sem))
    {
        return true;
    }
    return false;
}
/**
 *  \brief  释放信号量
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   使信号量值增加1,释放资源
 */
bool Semaphore::post()
{
    if (0==sem_post(&m_sem))
    {
        return true;
    }
    return false;
}
/**
 *  \brief  获取信号量值
 *  \param  value 当前值
 *  \return 成功返回true,失败返回false
 *  \note   当value>0说明有资源,=0无资源,<0表示有|value|个线程在等待资源
 */
bool Semaphore::getValue(int& value)
{
    if (-1==sem_getvalue(&m_sem,&value))
    {
        return false;
    }
    return true;
}
}